#!/usr/bin/env python

import cairo
from colourdb import colourdb
from math import *
from shapereduce import pairwise

class MercatorProjection(object):
    # latitude : n/s -> y
    # longitude : e/w -> x
    @classmethod
    def translate(cls, lat, lon):
        x = lon
        z = atan(sinh(lat))
        y = log(tan(z) + 1./cos(z))
        return (x, y)

class Renderer(object):
    def __init__(self):
        pass

    @classmethod
    def interpolate(cls, p1, p2, n):
        x1, y1 = p1
        x2, y2 = p2
        dx = (x2 - x1) / (n-1)
        dy = (y2 - y1) / (n-1)
        for i in xrange(n):
            yield x1 + (i * dx), x2 + (i * dy)


def in_radians(degrees):
    return degrees/180. * pi

def tuples_to_radians(l):
    for tp in l:
        yield [in_radians(t) for t in tp]

class CairoRenderer(Renderer):
    def __init__(self, width, padding, range, projection):
        self.lat_min, self.lat_max, self.lon_min, self.lon_max = map(in_radians, range)

        proj_min_x, proj_min_y = projection.translate(self.lat_min, self.lon_min)
        proj_max_x, proj_max_y = projection.translate(self.lat_max, self.lon_max)

        proj_width = proj_max_x - proj_min_x
        proj_height = proj_max_y - proj_min_y

        print "min lat", self.lat_min, "->", proj_min_x
        print "max lat", self.lat_max, "->", proj_max_x
        print proj_width, proj_height

        self.itr = sqrt(proj_width**2 + proj_height**2) / 1000.

        a = (proj_height / proj_width)
        height = int(abs(a) * width + 0.5)

        dx = width / proj_width
        dy = height / proj_height

        self.width = width + 2*padding
        self.height = height + 2*padding

        print "width=%d height=%d" % (self.width, self.height)

        self.sur = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)

        def translate(lat, lon):
            px, py = projection.translate(lat, lon)
            x = padding + (dx * (px - proj_min_x))
            y = self.height - padding - (dy * (py - proj_min_y))
            if x < 0:
                raise Exception("off left x=%d lon=%f" % (x, lon))
            elif x > self.width:
                raise Exception("off right x=%d lon=%f" % (x, lon))
            if y < 0:
                raise Exception("off top y=%d lat=%f" % (y, lat))
            elif y > self.height:
                raise Exception("off bottom y=%d lat=%f" % (y, lat))
            return x, y
        self.translate = translate

        print self.translate(self.lat_min, self.lon_min)
        print self.translate(self.lat_max, self.lon_max)


    def line(self, colour, points, line_width=2):
        def __line(a, b):
            x1, y1 = a
            x2, y2 = b
            w = x2 - x1
            h = y2 - y1
            it = int((sqrt(w**2 + h**2) / self.itr)) + 2
            dx = w / (it-1)
            dy = h / (it-1)
            for i in xrange(it):
##                print "line", x1 + i * dx, y1 + i * dy
                x, y = self.translate(x1 + i * dx, y1 + i * dy)
                cr.line_to(x, y)

        cr = cairo.Context(self.sur)
        if type(colour) == str:
            colour = colourdb[colour]
        cr.set_source_rgba(*colour)
        cr.set_line_width(line_width)

        for idx, (a, b) in enumerate(pairwise(points)):
            if idx == 0:
                cr.move_to(*self.translate(*a))
            __line(a, b)
        cr.stroke()

    def bounding_box(self):
        cr = cairo.Context(self.sur)
        cr.set_source_rgba(1., 1., 1., 1.)
        cr.paint()
        cr.set_source_rgba(0., 0., 0., 1.)
        cr.rectangle(0, 0, self.width, self.height)
        cr.stroke()
 
    def output(self, f):
        self.sur.write_to_png(f)


# vim: expandtab:ts=4:sw=4:
